package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.wms.cache.LocationCache;
import com.arpa.wms.cache.UnitCache;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.InventoryDTO;
import com.arpa.wms.domain.dto.PrepackagedDistributionDTO;
import com.arpa.wms.domain.dto.PrepackagedItemDTO;
import com.arpa.wms.domain.entity.Inventory;
import com.arpa.wms.domain.entity.Prepackaged;
import com.arpa.wms.domain.entity.PrepackagedDistribution;
import com.arpa.wms.domain.entity.PrepackagedItem;
import com.arpa.wms.domain.enums.InventoryRecordTypeEnum;
import com.arpa.wms.domain.enums.PrepackagedStatusEnum;
import com.arpa.wms.domain.vo.InventoryWithDistributionVO;
import com.arpa.wms.domain.vo.PrepackagedItemVO;
import com.arpa.wms.domain.vo.PrepackagedItemWithDistributionVO;
import com.arpa.wms.domain.vo.UsefulInventoryVO;
import com.arpa.wms.mapper.PrepackagedDistributionMapper;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.NumConst.NUM_ZERO;

/**
 * <p>
 * 预打包分配 服务实现类
 * </p>
 *
 * @author leo
 * @since 2021-07-02
 */
@Service
public class PrepackagedDistributionServiceImpl extends ServiceImpl<PrepackagedDistributionMapper, PrepackagedDistribution> implements IPrepackagedDistributionService {
    @Autowired
    private UnitCache unitCache;
    @Autowired
    @Lazy
    private IPrepackagedService prepackagedService;
    @Autowired
    private IPrepackagedItemService prepackagedItemService;
    @Autowired
    private IInventoryService inventoryService;
    @Autowired
    private IInventoryRecordService inventoryRecordService;
    @Autowired
    private LocationCache locationCache;
    /**
     * 根据预打包表code获取分配与明细关联列表
     * @param prepackagedCode
     * @return
     */
    @Override
    public List<PrepackagedItemWithDistributionVO> queryWithItemList(String prepackagedCode){
        List<PrepackagedItemWithDistributionVO> prepackagedItemWithDistributionVO = this.baseMapper.queryWithItemList(prepackagedCode,UserUtil.getBranchCode());

        // 过滤、翻译
        prepackagedItemWithDistributionVO.stream().forEach(e->{
            e.setGoodsUnitName(unitCache.translate(e.getGoodsUnit()));
            e.setLocationName(locationCache.translate(e.getLocation()));

        });
        //todo 库存数量回写
        return prepackagedItemWithDistributionVO;
    }

    /**
     * 自动分配
     * @param prepackagedCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result autoDistribute(String prepackagedCode){
        Prepackaged prepackaged = prepackagedService.getOne(new LambdaQueryWrapper<Prepackaged>().eq(Prepackaged::getCode, prepackagedCode).eq(Prepackaged::getGroupCode, UserUtil.getBranchCode()));
        // 查询获取明细
        List<PrepackagedItem> prepackagedItems = prepackagedItemService.list(new LambdaQueryWrapper<PrepackagedItem>()
                .eq(PrepackagedItem::getPrepackagedCode, prepackagedCode)
                .eq(PrepackagedItem::getGroupCode,UserUtil.getBranchCode()));
        // 过滤掉已完全分配的明细，即保留明细总数-分配数量 >0的数据
        prepackagedItems = prepackagedItems.stream().filter(e->
                e.getDistributionQuantity()!=null && e.getTotalQuantity().subtract(e.getDistributionQuantity()).compareTo(BigDecimal.ZERO) > 0
        ).collect(Collectors.toList());

        InventoryDTO inventoryDTO;
        // 预打包单新锁定数量
        BigDecimal totalNewLockQuantity = BigDecimal.ZERO;
        // 遍历这些待分配明细，获取库存，分配库存
        for(PrepackagedItem prepackagedItem: prepackagedItems){
            inventoryDTO = new InventoryDTO();
            inventoryDTO.setWarehouseCode(prepackaged.getWarehouseCode());
            inventoryDTO.setShipmentCode(prepackaged.getShipmentCode());
            inventoryDTO.setGoodsCode(prepackagedItem.getGoodsCode());
            inventoryDTO.setIsLock(NUM_ZERO);
            // 剩余待分配数量
            BigDecimal remainQuantity = prepackagedItem.getTotalQuantity().subtract(prepackagedItem.getDistributionQuantity());
            // 库存可用数量
            BigDecimal usefulQuantity;
            // 待锁定数量
            BigDecimal lockQuantity;
            // 明细新增待锁定数量
            BigDecimal totalItemNewLockQuantity = BigDecimal.ZERO;

            List<UsefulInventoryVO> inventories = inventoryService.getCanAutoDistributedInventory(inventoryDTO, NUM_ZERO);

            PrepackagedDistribution distribution;
            Inventory distributeInventory = new Inventory();
            Inventory tempInventory;
            // 遍历获取明细，根据明细的总数-分配数量>0的即需要分配的
            if (IterUtil.isNotEmpty(inventories)) {
                for(UsefulInventoryVO inventory: inventories){
                    usefulQuantity = inventory.getQuantity().subtract(inventory.getUseQuantity());
                    lockQuantity = usefulQuantity.compareTo(remainQuantity)>0?remainQuantity:usefulQuantity;
                    // 更新主子表新锁定数量
                    totalItemNewLockQuantity = totalItemNewLockQuantity.add(lockQuantity);
                    totalNewLockQuantity = totalNewLockQuantity.add(lockQuantity);

                    // 插入或更新分配表
                    distribution = insertOrUpdateDistribution(inventory, prepackagedItem, lockQuantity);

                    // 减去库存
                    reduceInventory(distribution);

                    BeanUtil.copyProperties(inventory, distributeInventory);
                    // 增加临时库存
                    tempInventory = savePrepackagedTempInventory(distribution, prepackaged, distributeInventory);

                    // 临时库位中的库存作业状态改为工作中，避免其他操作使用
                    inventoryService.setTempInvJobStatusWorking(tempInventory.getCode());

                    // 记录库存变动日志， 原库存减少，临时库存增加
                    inventoryRecordService.insertRecord(distributeInventory, prepackaged.getCode(), distribution.getDistributionQuantity().negate(), InventoryRecordTypeEnum.DELIVERY.getValue());
                    inventoryRecordService.insertRecord(tempInventory, prepackaged.getCode(), distribution.getDistributionQuantity(), InventoryRecordTypeEnum.DELIVERY.getValue());

                    // 需要回填发货分配的库存，将库存编码指向临时库存
                    update(new UpdateWrapper<PrepackagedDistribution>().lambda()
                            .eq(PrepackagedDistribution::getCode, distribution.getCode())
                            .set(PrepackagedDistribution::getInventoryCode, tempInventory.getCode())
                            .set(PrepackagedDistribution::getOldInventoryCode,inventory.getCode())
                    );

                    // 更新剩余数量
                    remainQuantity = remainQuantity.subtract(lockQuantity);

                    // 剩余分配数量为0，则说明完成分配，提前退出循环
                    if(remainQuantity.compareTo(BigDecimal.ZERO) == 0){
                        break;
                    }
                }
            }

            // 更新明细的分配数量和可用库存数量
            if(totalItemNewLockQuantity.compareTo(BigDecimal.ZERO) > 0) {
                PrepackagedItemDTO prepackagedItemDTO = new PrepackagedItemDTO();
                prepackagedItemDTO.setCode(prepackagedItem.getCode());
                prepackagedItemDTO.setDistributionQuantity(totalItemNewLockQuantity);
                prepackagedItemService.adjustDistributionQuantity(prepackagedItemDTO);
            }
        }
        // 更新预打包单分配数量和分配包裹数
        Integer distributionPackQuantity = totalNewLockQuantity.divide(prepackaged.getTotalCombinationQuantity(), 0, BigDecimal.ROUND_HALF_UP).intValue();
        prepackagedService.update(new UpdateWrapper<Prepackaged>().lambda()
                .eq(Prepackaged::getCode, prepackagedCode)
                .setSql(" distribution_quantity = distribution_quantity + " + totalNewLockQuantity)
                .setSql("distribution_pack_quantity = distribution_pack_quantity + " + distributionPackQuantity));

        prepackaged = prepackagedService.getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, prepackaged.getCode()).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));

        // 如果预打包单完全分配，则触发完全确认操作
        if (prepackaged.getTotalQuantity().compareTo(prepackaged.getDistributionQuantity()) == 0) {
            // 调用确认功能
            prepackagedService.confirm(prepackagedCode);
        } else {
            // 如果数量不足，但有分配，则状态调整为部分分配
            if (prepackaged.getDistributionQuantity().compareTo(BigDecimal.ZERO) > 0) {
                prepackagedService.update(new UpdateWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, prepackaged.getCode())
                        .eq(Prepackaged::getGroupCode,UserUtil.getBranchCode())
                        .set(Prepackaged::getStatus, PrepackagedStatusEnum.PART_DISTRIBUTION.getValue()));
            }
        }

        return Result.ok();
    }

    /**
     * 插入或更新分配表，根据库存、分配明细、待锁定数量
     * @param inventory
     * @param prepackagedItem
     * @param lockQuantity
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public PrepackagedDistribution insertOrUpdateDistribution(UsefulInventoryVO inventory, PrepackagedItem prepackagedItem, BigDecimal lockQuantity){
        // 查询是否有相同分配数据且未生成拣货单，有则在起基础上增加，没有则插入一条新的
        PrepackagedDistribution sameDistribution = this.getOne(new QueryWrapper<PrepackagedDistribution>().lambda()
                .eq(PrepackagedDistribution::getPrepackagedItemCode, prepackagedItem.getCode())
                .eq(PrepackagedDistribution::getOldInventoryCode, inventory.getCode())
                .eq(PrepackagedDistribution::getDeleted, 0));
        if(null == sameDistribution){
            // 插入新的分配数据
            sameDistribution = new PrepackagedDistribution();
            BeanUtil.copyProperties(inventory, sameDistribution);
            sameDistribution.setCode(IdUtil.simpleUUID());
            sameDistribution.setInventoryCode(inventory.getCode());
            sameDistribution.setGoodsUnit(prepackagedItem.getGoodsUnit());
            sameDistribution.setGoodsUnitName(prepackagedItem.getGoodsUnitName());
            sameDistribution.setPrepackagedCode(prepackagedItem.getPrepackagedCode());
            sameDistribution.setPrepackagedItemCode(prepackagedItem.getCode());
            sameDistribution.setDistributionQuantity(lockQuantity);
            sameDistribution.setOldInventoryCode(inventory.getCode());
            this.save(sameDistribution);
        }else{
            PrepackagedDistributionDTO distributionDTO = new PrepackagedDistributionDTO();
            distributionDTO.setCode(sameDistribution.getCode());
            distributionDTO.setDistributionQuantity(lockQuantity);
            this.baseMapper.adjustDistributeQuantity(distributionDTO);
        }

        return sameDistribution;
    }

    /**
     * 减少库存
     * @param distribution
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public Result reduceInventory(PrepackagedDistribution distribution){
        // 库存变动量 = 分配数量
        BigDecimal changeQuantity = distribution.getDistributionQuantity();

        // 原库存上扣除已分配的库存, 将变动量转为负数，用于减库存
        //boolean flag = inventoryService.adjustQuantity(distribution.getInventoryCode(), changeQuantity.negate());
        boolean flag =
                inventoryService.reduceQuantity(distribution.getInventoryCode(), changeQuantity);
        if (!flag) {
            throw new ServiceException("分配数量超过了可用数量，无法分配");
        }
        return Result.ok();
    }

    /**
     * 分配时，保存分配临时库存信息
     * @param distribution
     * @param prepackaged
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public Inventory savePrepackagedTempInventory(PrepackagedDistribution distribution, Prepackaged prepackaged,Inventory inventory){
        // 库存变动量 = 分配数量
        BigDecimal changeQuantity = distribution.getDistributionQuantity();
        // 判断是否有相同的临时库存，如果有，则在其基础上增加数量，如果没有，则新增一条
        Inventory sameInventory = inventoryService.hasSamePrepackagedTempInventory(distribution);
        if(sameInventory == null){
            sameInventory = inventoryService.savePrepackagedTempInventory(distribution, prepackaged,inventory);
        }else{
            // 增加临时库存
            inventoryService.adjustQuantity(sameInventory.getCode(), changeQuantity);
        }

        return sameInventory;
    }

    /**
     * @Description: 批量保存分配库存
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result batchSave(PrepackagedItemVO prepackagedItemVO){
        List<InventoryWithDistributionVO> temp = prepackagedItemVO.getInventoryWithDistributionVOList();
        //　清除掉分配数量是空的
        List<InventoryWithDistributionVO> inventoryWithDistributionVOS = temp.stream().filter(e->
                e.getDistributionQuantity() != null && e.getDistributionQuantity().compareTo(BigDecimal.ZERO) > 0
        ).collect(Collectors.toList());
        // 如果没有分配的数据，则直接返回，不再处理
        if (IterUtil.isEmpty(inventoryWithDistributionVOS)) {
            return Result.ok();
        }

        // 判断总数是否超过待分配库存
        PrepackagedItem prepackagedItem = prepackagedItemService.getOne(new QueryWrapper<PrepackagedItem>().lambda().eq(PrepackagedItem::getCode, prepackagedItemVO.getCode()));
        BigDecimal remainQuantity = prepackagedItem.getTotalQuantity().subtract(prepackagedItem.getDistributionQuantity());

        // 判断新增的分配数量是否超过允许数量
        BigDecimal newDistributionQuantity = inventoryWithDistributionVOS.stream().map(InventoryWithDistributionVO::getDistributionQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        if(newDistributionQuantity.compareTo(remainQuantity) > 0){
            return Result.error("分配数量超过剩余分配数量");
        }

        Prepackaged prepackaged = prepackagedService.getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, prepackagedItemVO.getPrepackagedCode()));
        Inventory tempInventory;

        // 锁定库存
        for(InventoryWithDistributionVO e: inventoryWithDistributionVOS){
            UsefulInventoryVO usefulInv = new UsefulInventoryVO();
            BeanUtil.copyProperties(e, usefulInv);
            // 赋值基本单位信息
            usefulInv.setBasicUnit(e.getGoodsUnit());
            usefulInv.setBasicUnitName(e.getUnitName());
            PrepackagedItem item = new PrepackagedItem();
            BeanUtil.copyProperties(prepackagedItemVO, item);
            // 生成分配单（有相同分配单的，更新相同分配单，没有则新增）
            PrepackagedDistribution sameDistribution = insertOrUpdateDistribution(usefulInv, item, e.getDistributionQuantity());

            // 正式库存减少
            reduceInventory(sameDistribution);

            // 用于分配的库存
            Inventory distributeInventory = new Inventory();
            // 临时库存增加
            BeanUtil.copyProperties(e, distributeInventory);
            tempInventory = savePrepackagedTempInventory(sameDistribution, prepackaged, distributeInventory);

            // 临时库存标记作业状态、 添加库存日志
            inventoryService.setTempInvJobStatusWorking(tempInventory.getCode());
            inventoryRecordService.insertRecord(sameDistribution.getInventoryCode(), sameDistribution.getPrepackagedCode(),
                    sameDistribution.getDistributionQuantity().negate(), InventoryRecordTypeEnum.DELIVERY.getValue());
            inventoryRecordService.insertRecord(tempInventory, sameDistribution.getPrepackagedCode(),
                    sameDistribution.getDistributionQuantity(), InventoryRecordTypeEnum.DELIVERY.getValue());

            // 需要回填发货分配的库存，将库存编码指向临时库存
            update(new UpdateWrapper<PrepackagedDistribution>().lambda().eq(PrepackagedDistribution::getCode, sameDistribution.getCode())
                    .set(PrepackagedDistribution::getInventoryCode, tempInventory.getCode())   //新库存编码
                    .set(PrepackagedDistribution::getOldInventoryCode,e.getCode()));   //旧的库存编码
        }

        // 更新明细上的分配数量
        PrepackagedItemDTO prepackagedItemDTO = new PrepackagedItemDTO();
        prepackagedItemDTO.setCode(prepackagedItemVO.getCode());
        prepackagedItemDTO.setDistributionQuantity(newDistributionQuantity);
        prepackagedItemService.adjustDistributionQuantity(prepackagedItemDTO);

        // 更新发货单分配数量
        BigDecimal newTotalDistributionQuantity = prepackaged.getDistributionQuantity().add(newDistributionQuantity);
        Integer distributionPackQuantity = newTotalDistributionQuantity.divide(prepackaged.getTotalCombinationQuantity(), 0, BigDecimal.ROUND_HALF_UP).intValue();
        LambdaUpdateWrapper<Prepackaged> updateWrapper = new UpdateWrapper<Prepackaged>().lambda();
        updateWrapper.eq(Prepackaged::getCode, prepackaged.getCode());
        updateWrapper.setSql(" distribution_quantity = distribution_quantity + " + newDistributionQuantity);
        updateWrapper.set(Prepackaged::getDistributionPackQuantity, distributionPackQuantity);
        if(prepackaged.getTotalQuantity().compareTo(newTotalDistributionQuantity) > 0) {
            // 总数未满，则更新为部分分配（分配数量为0的已在最上面过滤返回了）
            updateWrapper.set(Prepackaged::getStatus, PrepackagedStatusEnum.PART_DISTRIBUTION.getValue());
            prepackagedService.update(updateWrapper);
        }else{
            // 数量已满，则只更新数量，调用确认生成拣货单时会调整状态的
            prepackagedService.update(updateWrapper);
            // 确认生成拣货单
            prepackagedService.confirm(prepackaged.getCode());
        }

        return Result.ok();
    }

    /**
     * 查询是否有相同的分配信息
     *
     * @param itemCode
     * @param distributionCode
     * @return
     */
    @Override
    public PrepackagedDistribution hasSameDistribution(String itemCode, String distributionCode) {
        // 根据明细code,获取其他分配信息
        List<PrepackagedDistribution> otherDistributions = list(new QueryWrapper<PrepackagedDistribution>().lambda()
                .eq(PrepackagedDistribution::getPrepackagedItemCode, itemCode).ne(PrepackagedDistribution::getCode, distributionCode)
        .eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode()));
        if (IterUtil.isEmpty(otherDistributions)) {
            return null;
        }

        PrepackagedDistribution distribution = getOne(new QueryWrapper<PrepackagedDistribution>().lambda().eq(PrepackagedDistribution::getCode, distributionCode).eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode()));
        // 判断是否有相同库存号
        otherDistributions = otherDistributions.stream().filter(e->e.getInventoryCode().equals(distribution.getInventoryCode())).collect(Collectors.toList());
        if (IterUtil.isNotEmpty(otherDistributions)) {
            return otherDistributions.get(NumConst.NUM_ZERO);
        }

        return null;
    }

    /**
     * 根据分配表code， 调整分配数量，正数表示增加，负数表示减少
     * @param prepackagedDistributionDTO
     * @return
     */
    @Override
    public int adjustDistributionQuantity(PrepackagedDistributionDTO prepackagedDistributionDTO) {
        return this.baseMapper.adjustDistributeQuantity(prepackagedDistributionDTO);
    }

    /***
     * @description
     * 根据预打包单号，更新预打包分配单拣货数量
     * @author rfwang
     * @date 2021年7月7日
     * @param prepackagedCode:
     **/
    @Override
    public int updatePickingQuantity(String prepackagedCode){
        return this.baseMapper.updatePickingQuantity(prepackagedCode);
    }
}
